﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ValiVIN.Manufacturer
{
    // http://www.nissanhelp.com/diy/common/nissan_vin.htm
    // From http://www.pbase.com/image/19276707 , it seems that combination of char 5 + char 6 = Model / Series x.x
    // Sample VIN: 1N6SD11Y6SC343885
    
    internal class Nissan : IVINManufacturer
    {
        readonly int CurrentYear = DateTime.Now.Year + 1; // usually car year is ahead of us 1 year x.x, so +1 here

        static bool IsYearBetween(int year, int year1, int year2)
        {
            return year >= year1 || year <= year2;
        }

        #region Singleton

        static readonly Nissan _instance = new Nissan();

        public static Nissan Instance
        {
            get { return _instance; }
        }

        #endregion

        #region IVINManufacturer member

        public bool Decode(VINumber vin)
        {
            DecodeLine(vin);
            //GetVehicleLine2(vin);
            GetModelChange(vin);
            GetBodyType(vin);
            GetMisc(vin);
            GetPlant(vin);
            GetSerial(vin);
            return true;
        }

        public string ManufacturerName(string vin)
        {
            string value = string.Empty;
            string code = vin.Substring(0, 3);

            switch (code)
            {
                case "1N4": value = "USA Nissan Vehicle"; break;
                case "1N6": value = "USA Nissan Truck"; break;
                case "5N1": value = "USA Nissan Wagon"; break;
                case "3N1": value = "Mexico (Assembly Point) Nissan Vehicle"; break;

                // TODO: Figure out where is 4N2 manufacturer is ... 4N2DN11W2TD836415
                // http://en.wikipedia.org/wiki/Vehicle_Identification_Number#World_Manufacturer_Identifier  
                // from the wiki, 4A-4O is united state ... so is 5A-5O
                case "4N2": value = "Not certain, possibly Nissan vehicle"; break; // seen this from Nissan Quest 1996+
                case "JN1": value = "Japan Nissan Vehicle"; break;
                case "JN3": value = "Japan Nissan Convertable"; break;
                case "JN6": value = "Japan Nissan Vehicle Van"; break;
                case "JN8": value = "Japan Nissan Multi-purpose Vehicle"; break;
            }

            if (value.Equals(string.Empty))
            {
                code = vin.Substring(0, 2);

                switch (code)
                {
                    case "1N": 
                    case "5N":value = "USA Nissan"; break;
                    case "JN": value = "USA Japan"; break;

                    // there are nissan plant in mexico, and I guess they assemble in mexico? then could be port to U.s??
                    // http://www.nissanusa.com/about/corporate-info/nissan-in-north-america.html  <-- listed nissan mexicana
                    // majority of modern nissan sentra (from ebay) used 3N1 code [ex: 3N1AB6AP8AL643541] [2010 nissan sentra]
                    // http://www.b15sentra.net/forums/archive/index.php/t-13170.html   
                    case "3N": value = "Mexico (Assembly Point) Nissan"; break; 
                }
            }

            return value;
        }

        #endregion

        #region Engine Decoder

        // http://en.wikipedia.org/wiki/List_of_Nissan_engines

        string DecodeEngine(string engine)
        {
            if (engine.Length < 2)
                return string.Empty;

            int index = 0;
            string tempFamily = string.Empty;
            string family = string.Empty;
            string displacement = string.Empty;
            string feature = string.Empty;

            // TODO: decode engine type, for example, if engine starts with VG, that's V-6 family line, 
            // but there's so many of them so I'm lazy x.x
            // handle engine type


            // if char 2 is number, then engine family only has 1 letter
            if (char.IsNumber(engine[1]))
            {
                tempFamily = engine[0].ToString();
                index = 1;
            }
            else
            {
                tempFamily = engine.Substring(0, 2);
                index = 2;
            }

            family = DecodeEngineFamily(tempFamily);

            // handle engine displacement
            // It appears that engine displacement has 2 digit most of the time, I will use fixed length method
            displacement = engine.Substring(index, 2).Insert(1, ".") + "L";
            index += 2;

            // hanlde engine feature
            feature = DecodeEngineFeature(engine.Substring(index));

            return
                family + ", " +
                displacement + ", " +
                feature;
        }

        string DecodeEngineFamily(string code)
        {
            string value = string.Empty;

            switch (code)
            {
                case "CA":
                case "QR":
                case "SR":
                case "KA": value = "Straight-4"; break;

                case "VG":
                case "VE":
                case "VQ":
                case "VR": value = "V-6"; break;

                case "VH":
                case "VK": value = "V-8"; break;
            }

            return value;
        }

        string DecodeFeature(string code)
        {
            string value = string.Empty;

            switch (code)
            {
                case "D": value = "DOHC"; break;
                case "DD": value = "Direct Injection"; break;
                case "E": value = "Electronic Fuel Injection"; break;
                case "HR": value = "High Response & High Revolution"; break;
                case "N": value = "Natural Gas fuelled"; break;
                case "i": value = "Throttle Body Fuel Injection"; break;
                case "P": value = "LPG fuelled"; break;
                case "R": value = "Supercharged"; break;
                case "S": value = "Carburettor"; break;
                case "T": value = "Turbocharged"; break;
                case "Ti": value = "Turbocharged and intercooled"; break;
                case "TT": value = "Twin Turbocharged"; break;
                case "V": value = "Variable Valve Timing with Lift"; break;
            }

            return value;
        }

        // http://en.wikipedia.org/wiki/List_of_Nissan_engines 

        string DecodeEngineFeature(string feature)
        {
            StringBuilder sb = new StringBuilder();
            string value = string.Empty;

            for (int i = 0; i < feature.Length; i++)
            {
                // handle 2 chars feature
                if (i + 1 < feature.Length)
                {
                    string fchars = feature[i].ToString() + feature[i + 1].ToString();
                    value = DecodeFeature(fchars);
                    if (value != string.Empty)
                    {
                        sb.Append(value);
                        sb.Append(",");
                        i++;
                        continue;
                    }
                }

                string fchar = feature[i].ToString();
                value = DecodeFeature(fchar);
                if (value != string.Empty)
                {
                    sb.Append(value);
                    sb.Append(",");
                }
            }

            return sb.ToString();
        }

        #endregion

        #region Line Decoder

        /// <summary>
        /// What Line Decoder is missing:
        /// Europe Models
        /// Diesel Engines
        /// </summary>
        /// <param name="vin"></param>

        void DecodeLine(VINumber vin)
        {
            string engineCode = vin.VIN[3].ToString();
            string lineCode = vin.VIN[4].ToString();
            string modelCode = vin.VIN[5].ToString();
            string bodyCode = vin.VIN[6].ToString();
            string lineModelCode = lineCode + modelCode;

            int year = vin.Year;
            string line = string.Empty;
            string line2 = string.Empty;
            string engine = string.Empty;

            StringBuilder sb = new StringBuilder();

            switch (lineModelCode)
            {
                // GT-R
                case "R5": // 2008, current
                    line = "GT-R";
                    engine = "VR38DETT";
                    break;

                // 370z
                case "Z4": // 2009, current
                    line = "370z";
                    engine = "VQ37VHR";
                    break;

                // 350z
                case "Z3":
                    line = "350z";

                    switch (engineCode)
                    {
                        case "A": engine = "VQ35DE"; break; // 2003, 2006
                        case "B": engine = "VQ35HR"; break; // 2007, 2009
                    }
                    break;

                // COLLAPSE: 300zx or cube
                case "Z2":
                    switch (bodyCode)
                    {
                        // Cube
                        case "K":
                        case "8":
                            line = "Cube"; // 2008, current
                            engine = "MR18DE";
                            break;

                        // 300zx
                        case "4":
                        case "6":
                            line = "300zx"; // 1990, 1996

                            switch (engineCode)
                            {
                                case "C": engine = "VG30DETT"; break;
                                case "R": engine = "VG30DE"; break;
                            }
                            break;
                    }
                    break;

                // COLLAPSE: 300zx or Murano
                case "Z1":
                    switch (bodyCode)
                    {
                        // 300zx
                        case "4":
                        case "6":
                            line = "300zx"; // 1984, 1989

                            switch (engineCode)
                            {
                                case "C": engine = "VG30ET"; break;
                                case "H": engine = "VG30E"; break;
                            }
                            break;

                        // Murano
                        case "8":
                            line = "Murano"; // 2008, current
                            engine = "VQ35DE";
                            break;
                    }
                    break;

                // Murano
                case "Z0":
                    line = "Murano";
                    line2 = "Z50";
                    engine = "VQ35DE";
                    break;

                // 240sx
                case "S4": // 1995, 1998
                    line = "240sx";
                    engine = "KA24DE";
                    break;

                case "S3": // 1989, 1994
                    line = "240sx";

                    switch (engineCode)
                    {
                        case "M": engine = "KA24DE"; break; // 1991, 1994
                        case "H": engine = "KA24E"; break; // 1989, 1990
                    }
                    break;

                // 200sx
                case "S2": // 1984, 1988
                    line = "200sx";

                    switch (engineCode)
                    {
                        case "C": engine = "CA18ET"; break; // 1984, 1986
                        case "P": engine = "CA20E"; break;
                        case "V": engine = "VG30E"; break;
                    }
                    break;

                // COLLAPSE: 200sx or sentra
                case "B4": // 1995, 1999
                    switch (bodyCode)
                    {
                        case "2": line = "200sx"; break;
                        case "1": line = "Sentra"; break;
                    }

                    switch (engineCode)
                    {
                        case "A": engine = "GA16DE"; break;
                        case "B": engine = "SR20DE"; break;
                    }

                    switch (engineCode + bodyCode)
                    {
                        case "B2": line2 = "SE-R"; break;
                        case "B1": line2 = "B14, SE"; break;
                        case "A1": line2 = "B14, XE or GXE"; break;
                    }
                    break;

                // Sentra
                case "B6": // 2007, current
                    line = "Sentra";

                    switch (engineCode)
                    {
                        case "A": engine = "MR20DE"; line2 = "B16"; break;
                        case "B": engine = "QR25DE"; line2 = "B16, SE-R"; break;
                        case "C": engine = "QR25DE"; line2 = "B16, SE-R Spec-V"; break;
                    }
                    break;

                case "B5": // 2000, 2006
                    line = "Sentra";

                    switch (engineCode)
                    {
                        case "A": // 2002, 2006
                            engine = "QR25DE";
                            line2 = "B15, SE-R, maybe Spec-V, hard to tell";
                            break;
                        case "B": // 2000, 2001
                            engine = "SR20DE";
                            line2 = "B15, SE";
                            break;
                        case "C": // 2000, 2006
                            engine = "QG18DE";
                            line2 = "B15, XE or GXE";
                            break;
                    }
                    break;

                // Maxima
                case "A5": // 2009, current
                case "A4": // 2004, 2008
                    line = "Maxima";
                    engine = "VQ35DE";
                    break;

                case "A3":
                    line = "Maxima";

                    switch (engineCode)
                    {
                        case "D": engine = "VQ35DE"; break; // 2002, 2003
                        case "C": engine = "VQ30DE"; line2 = "A33"; break; // 2000, 2001
                    }
                    break;

                case "A2": // 1995, 1999
                    line = "Maxima";
                    line2 = "A32";
                    engine = "VQ30DE";
                    break;

                case "J0": // 1992, 1994
                    line = "Maxima";
                    engine = "VE30DE";
                    break;

                // Altima
                case "L2": // 2007, current
                    line = "Altima";

                    switch (engineCode)
                    {
                        case "A": engine = "QR25DE"; break;
                        case "B": engine = "VQ35DE"; break;
                        case "C": engine = "QR25DE"; line2 = "Hybrid"; break;
                    }
                    break;

                case "L1": // 2002, 2006
                    line = "Altima";

                    switch (engineCode)
                    {
                        case "A": engine = "QR25DE"; break;
                        case "B": engine = "VQ35DE"; line2 = "maybe SE-R"; break;
                    }
                    break;

                case "L0": // 1998, 2001
                case "U3": // 1993, 1997
                    line = "Altima";
                    engine = "KA24DE";
                    break;

                // Versa
                case "C1": // 2004, current
                    line = "Versa";

                    switch (engineCode)
                    {
                        case "B": engine = "MR18DE"; break;
                        case "C": engine = "HR16DE"; break;

                        // TODO: figure out which code for HR15DE engine
                    }
                    break;

                // Pathfinder
                case "R1": // 2005, current
                    line = "Pathfinder";

                    switch (engineCode)
                    {
                        case "A": engine = "VQ40DE"; break;
                        case "B": engine = "VK56DE"; break;
                    }
                    break;

                case "R0": // 1996, 2004
                    line = "Pathfinder";

                    switch (engineCode)
                    {
                        case "A":
                            if (IsYearBetween(year, 1996, 2000))
                            {
                                engine = "VG33E";
                            }
                            else if (IsYearBetween(year, 2001, 2004))
                            {
                                engine = "VQ35DE";
                            }
                            break;
                        case "D": engine = "VQ35DE"; break;
                    }
                    break;

                case "D1": // 1986, 1995
                    line = "Pathfinder";

                    switch (engineCode)
                    {
                        case "H":
                            if (IsYearBetween(year, 1986, 1989))
                            {
                                engine = "VG30i";
                            }

                            else if (IsYearBetween(year, 1990, 1995))
                            {
                                engine = "VG30E";
                            }
                            break;
                    }
                    break;

                // Xterra
                case "N0": // 2005, current
                    line = "Xterra";
                    line2 = "N50";
                    engine = "VQ40DE";
                    break;

                // COLLAPSE: Xterra or Frontier
                case "D2": // 2000, 2004
                    switch (bodyCode)
                    {
                        case "8":
                            line = "Xterra";
                            line2 = "WD22";
                            break;
                        case "1":
                        case "6":
                        case "7":
                            line = "Frontier";
                            line2 = "D22";
                            break;
                    }

                    switch (engineCode)
                    {
                        case "M": engine = "VG33ER"; break; // xterra, 2002, 2004, frontier, 1998, 2004
                        case "D": engine = "KA24DE"; break; // xterra, 2000, 2004
                        case "E": engine = "VG33E"; break; // xterra, 2000, 2004
                    }
                    break;

                // Frontier
                case "D0":
                    line = "Frontier";
                    line2 = "D40";

                    switch (engineCode)
                    {
                        case "A": engine = "VQ40DE"; break;
                        case "B": engine = "QR25DE"; break;
                    }
                    break;

                // COLLAPSE: Armanda or Titan
                case "A0":
                    switch (bodyCode)
                    {
                        // Armanda
                        case "N": // 2010, current
                        case "8": // 2004, 2009
                            line = "Armanda";
                            engine = "VK56DE";
                            break;

                        // Titan
                        case "C": // 2010, current
                        case "E": // 2010, current
                        case "6": // 2004, 2009
                        case "7": // 2004, 2009
                            line = "Titan";
                            engine = "VK56DE";
                            break;
                    }
                    break;

                // Quest
                case "V2": // 2004, 2009
                    line = "Quest";
                    engine = "VQ35DE";
                    break;

                case "N1":
                    line = "Quest";

                    switch (engineCode)
                    {
                        case "X": engine = "VG33E"; break; // 1999, 2002
                        case "D": engine = "VG30E"; break; // 1993, 1998
                        case "Z":
                            if (IsYearBetween(year, 1993, 1998))
                            {
                                engine = "VG30E"; break;
                            }

                            else if (IsYearBetween(year, 1999, 2002))
                            {
                                engine = "VG33E"; break;
                            }
                            break;
                    }
                    break;

                // Rogue
                case "S5": // 2007, current
                    line = "Rogue";
                    engine = "QR25DE";
                    break;
            }

            // OUTPUT
            string name2 = string.Empty;
            if (!string.IsNullOrEmpty(line2))
            {
                name2 = "(" + line2 + ")";
            }

            sb.AppendLine();
            sb.AppendLine("Line#:" + line + " " + name2);

            if (engine != string.Empty)
            {
                sb.AppendFormat("Engine: [{0}] {1}", engine, DecodeEngine(engine));
                sb.AppendLine();
            }

            vin.ManufacturerSpecifics["Vehicle Line"] = sb.ToString();
        }

        #endregion

        // char 6 = model generation
        void GetModelChange(VINumber vin)
        {
            vin.ManufacturerSpecifics.Add("Model Number", vin.VIN[5].ToString());
        }

        // char 7 = Body Type
        void GetBodyType(VINumber vin)
        {
            string key = "Body Type";
            string value = string.Empty;
            string code = vin.VIN[6].ToString();

            switch (code)
            {
                case "1": value = "4-Door Sedan, Standard Body Truck"; break;
                case "2": value = "2-Door Coupe, Long Bed Truck"; break;
                case "4": value = "2-Door Coupe"; break;
                case "5": value = "4-Door Wagon"; break;
                case "6": value = "2-Door Convertible, Fastback, King Cab Truck"; break;
                case "7": value = "Crew Cab Truck"; break;
                case "8": value = "8- Door Wagon"; break;
            }

            vin.ManufacturerSpecifics[key] = value;
        }

        // char 8 = Miscellaneous
        void GetMisc(VINumber vin)
        {
            string key = "Miscellaneous";
            string value = string.Empty;
            string code = vin.VIN[7].ToString();

            switch (code)
            {
                case "A": value = "Convertible; 3-Point Passive Belts; Driver / Pass. SR & Air Bags, Side Air Bags"; break;
                case "C": value = "Air Bags"; break;
                case "D": value = "Air Bags w/3-Point Belts"; break;
                case "E": value = "Driver / Pass / Side SRS Air Bags, 3-Point Belts"; break;
                case "F": value = "Passive Belt & Air Bag"; break;
                case "P": value = "Auto / Passive Seat Belts"; break;
                case "S": value = "No Designation; 2-Wheel Drive; Non-Passive Belt"; break;
                case "T": value = "2-Wheel Drive, 4-Wheel ABS"; break;
                case "Y": value = "4-Wheel Drive, 4-Wheel ABS"; break;
            }

            vin.ManufacturerSpecifics[key] = value;
        }

        // char 11 = Plant Code
        void GetPlant(VINumber vin)
        {
            string code = vin.VIN[10].ToString();
            string value = string.Empty;

            switch (code)
            {
                case "C": value = "Smyrna, Tennessee, USA"; break;
                case "T": value = "Tochigi, Japan | Oppama, Japan"; break;
                case "M": value = "Tochigi, Japan"; break;
                case "N": value = "Canton, Mississippi, USA"; break;
                case "W": value = "Kyushyu, Japan"; break;
            }

            vin.Plant = value;
        }

        // char 12+ = serial number
        void GetSerial(VINumber vin)
        {
            vin.SerialNumber = vin.VIN.Substring(11);
        }
    }
}
